// server/server-function-hub.js
/**
 * Promise-Aware Server Function Hub
 * Enhanced to properly handle async functions and Promises
 */
const WebSocket = require('ws');
const https = require('https');
const fs = require('fs');
const path = require('path');
require('dotenv').config();

console.log('🚀 SERVER-FUNCTION-HUB STARTING UP...');
console.log('📁 Current working directory:', process.cwd());
console.log('🔧 Node version:', process.version);

// Import security helpers
console.log('🔐 Attempting to import security helpers...');
try {
  const { processAllEncryptedData } = require('./security-helpers');
  console.log('✅ Security helpers imported successfully');
  console.log('🔍 processAllEncryptedData function:', typeof processAllEncryptedData);
} catch (error) {
  console.log('❌ FAILED to import security helpers:', error.message);
  console.log('📍 Error stack:', error.stack);
  process.exit(1);
}

// Configuration
const WS_PORT = process.env.WS_PORT || 8080;
const SSL_PREFIX = process.env.SSL_PREFIX || '/etc/letsencrypt/live/';
const DOMAIN = process.env.DOMAIN || 'tuvme.xyz';

console.log('⚙️  Configuration loaded:');
console.log('   WS_PORT:', WS_PORT);
console.log('   DOMAIN:', DOMAIN);
console.log('   SSL_PREFIX:', SSL_PREFIX);

// SSL Certificate paths
const SSL_KEY_PATH = process.env.SSL_KEY_PATH || path.join(SSL_PREFIX, DOMAIN, 'privkey.pem');
const SSL_CERT_PATH = process.env.SSL_CERT_PATH || path.join(SSL_PREFIX, DOMAIN, 'fullchain.pem');

console.log('🔑 SSL Certificate paths:');
console.log('   KEY:', SSL_KEY_PATH);
console.log('   CERT:', SSL_CERT_PATH);

// Check if SSL files exist
console.log('🔍 Checking SSL files...');
try {
  fs.accessSync(SSL_KEY_PATH);
  console.log('✅ SSL Key file exists');
} catch (error) {
  console.log('❌ SSL Key file missing:', SSL_KEY_PATH);
}

try {
  fs.accessSync(SSL_CERT_PATH);
  console.log('✅ SSL Cert file exists');
} catch (error) {
  console.log('❌ SSL Cert file missing:', SSL_CERT_PATH);
}

/**
 * Enhanced logging function
 */
function log(label, data = null) {
  const timestamp = new Date().toISOString();
  console.log(`[${timestamp}] [SERVER-FUNCTION-HUB] ${label}`);
  if (data) {
    console.log(JSON.stringify(data, null, 2));
  }
}

/**
 * In-memory store for functions
 */
const functionStore = new Map();
console.log('📦 Function store initialized');

/**
 * Stats
 */
const stats = {
  totalConnections: 0,
  activeConnections: 0,
  registrations: 0,
  executions: 0,
  errors: 0
};
console.log('📊 Stats initialized:', stats);

/**
 * Register a function or code snippet
 */
function registerFunction(functionId, functionBody, paramNames = []) {
  console.log('🔧 REGISTERING FUNCTION:', functionId);
  console.log('📝 Function body length:', functionBody.length);
  console.log('📋 Parameter names:', paramNames);
  
  stats.registrations++;
  
  // Log full function body without any truncation
  log(`======= REGISTERING FUNCTION: ${functionId} =======`);
  console.log("FUNCTION BODY BEGIN:");
  console.log(functionBody);
  console.log("FUNCTION BODY END");
  log(`========================================`);

  // Store the code info without any validation
  functionStore.set(functionId, {
    body: functionBody,
    params: paramNames
  });

  console.log('✅ Function stored in functionStore');
  console.log('📊 Total functions in store:', functionStore.size);
  console.log('🔍 Function store keys:', Array.from(functionStore.keys()));

  log(`Code successfully registered: ${functionId}`);
  return true;
}

/**
 * Helper function to check if a value is a Promise or Promise-like
 */
function isPromiseLike(value) {
  const result = value && typeof value.then === 'function';
  console.log('🔍 isPromiseLike check:', typeof value, '→', result);
  return result;
}

/**
 * Execute registered function
 */
async function executeFunction(functionId, args = []) {
  console.log('🚀 EXECUTE FUNCTION CALLED');
  console.log('🔍 Function ID:', functionId);
  console.log('📊 Arguments count:', args.length);
  console.log('📋 Arguments:', args);
  
  stats.executions++;
  
  log(`======= EXECUTING FUNCTION: ${functionId} =======`);
  log(`Raw Arguments:`, args);

  console.log('🔍 Checking if function exists in store...');
  console.log('📦 Current function store size:', functionStore.size);
  console.log('🔑 Available function IDs:', Array.from(functionStore.keys()));
  console.log('🎯 Looking for:', functionId);
  console.log('✅ Function exists?', functionStore.has(functionId));

  if (!functionStore.has(functionId)) {
    console.log('❌ FUNCTION NOT FOUND!');
    log(`Code not found: ${functionId}`);
    const error = new Error(`Code not found: ${functionId}`);
    console.log('💥 Error object created:', error.message);
    throw error;
  }

  console.log('✅ Function found in store, proceeding with execution...');

  try {
    const funcInfo = functionStore.get(functionId);
    console.log('📝 Retrieved function info:', { 
      bodyLength: funcInfo.body.length, 
      params: funcInfo.params 
    });
    
    console.log('🔐 Starting encryption processing...');
    
    // NEW: Process and decrypt both function body and arguments
    const { processAllEncryptedData } = require('./security-helpers');
    console.log('🔍 processAllEncryptedData function type:', typeof processAllEncryptedData);
    
    console.log('🔄 Processing encrypted data...');
    const { processedBody, processedArgs } = processAllEncryptedData(funcInfo.body, args);
    console.log('✅ Encryption processing complete');
    
    // Log the processed arguments for debugging
    console.log('🔐 PROCESSED ARGS COMPARISON:');
    console.log('   Original args:', args);
    console.log('   Processed args:', processedArgs);
    log(`PROCESSED ARGS:`, processedArgs);

    // Log full stored function body
    log(`EXECUTING THIS EXACT CODE:`);
    console.log(processedBody);
    log(`========================================`);

    console.log('🏗️  Creating execution context...');

    // Create execution context
    const executeCode = (code, processedArgs) => {
      console.log('🔧 executeCode function called');
      console.log('📝 Code length:', code.length);
      console.log('📋 Args:', processedArgs);
      
      // Create a safe execution context with allowed Node.js modules
      const vm = require('vm');
      console.log('📦 VM module loaded');
      
      const sandbox = {
        require,
        process,
        console,
        Buffer,
        setTimeout,
        clearTimeout,
        setInterval,
        clearInterval,
        setImmediate,
        clearImmediate,
        args: processedArgs,  // Use decrypted arguments
        Promise // Explicit Promise reference
      };
      console.log('🏗️  Sandbox created with keys:', Object.keys(sandbox));
      console.log('📋 Sandbox args:', sandbox.args);
      
      // Create context and run code
      const context = vm.createContext(sandbox);
      console.log('🌍 VM context created');
      
      // FIXED: Instead of double-wrapping in IIFEs, check if the code is already
      // returning a Promise or has an async function signature.
      // If the code appears to be async, we'll just run it directly.
      let codeToRun = code;
      
      console.log('🔍 Analyzing code structure...');
      
      // Check if code appears to be an async IIFE already
      const isAsyncIIFE = /^\s*\(?\s*async\s+function/.test(code) || 
                           code.includes('new Promise');
      
      console.log('🔍 Code analysis results:');
      console.log('   isAsyncIIFE:', isAsyncIIFE);
      console.log('   contains async function:', /^\s*\(?\s*async\s+function/.test(code));
      console.log('   contains Promise:', code.includes('new Promise'));
      
      // If not an async IIFE already, but is a plain function or statement, wrap it
      if (!isAsyncIIFE) {
        console.log('🔧 Code is not an async IIFE, wrapping in function...');
        log(`Code is not an async IIFE, wrapping in function...`);
        codeToRun = `(function() { ${code} })()`;
      } else {
        console.log('✅ Code appears to be async/Promise-based, executing as-is');
        log(`Code appears to be async/Promise-based, executing as-is`);
      }
      
      log(`EXECUTING FINAL CODE:`);
      console.log('🚀 FINAL CODE TO EXECUTE:');
      console.log(codeToRun);
      
      console.log('⚡ Running code in VM context...');
      
      // Execute the code
      const result = vm.runInContext(codeToRun, context);
      
      console.log('✅ Code execution completed');
      console.log('🔍 Raw result type:', typeof result);
      console.log('📊 Raw result:', result);
      
      log(`Raw execution result type: ${typeof result}`);
      
      return result;
    };

    console.log('🎯 Calling executeCode...');
    
    // Execute the code with the processed arguments
    let result = executeCode(processedBody, processedArgs);
    
    console.log('📊 Execution result received:', typeof result);
    
    // Log the result type
    log(`EXECUTION RESULT TYPE: ${typeof result}`);
    
    // If result is a Promise or Promise-like, await it
    if (isPromiseLike(result)) {
      console.log('⏳ Result is Promise-like, awaiting...');
      log(`DETECTED PROMISE RESULT - AWAITING...`);
      try {
        console.log('⏳ Starting Promise resolution...');
        result = await result;
        console.log('✅ Promise resolved successfully');
        console.log('📊 Final resolved result:', result);
        log(`PROMISE RESOLVED TO:`, result);
      } catch (promiseError) {
        console.log('💥 Promise rejected with error:', promiseError);
        log(`PROMISE REJECTION:`, { 
          error: promiseError.message, 
          stack: promiseError.stack 
        });
        throw promiseError;
      }
    } else {
      console.log('✅ Result is not a Promise, using directly');
      log(`EXECUTION RESULT (NOT A PROMISE):`, result);
    }
    
    console.log('🎉 Function execution completed successfully');
    console.log('📊 Final result:', result);
    
    return result;
    
  } catch (error) {
    console.log('💥 ERROR in executeFunction:', error);
    console.log('📍 Error message:', error.message);
    console.log('📍 Error stack:', error.stack);
    
    stats.errors++;
    log(`ERROR EXECUTING CODE: ${functionId}`, { 
      errorType: error.constructor.name,
      errorMessage: error.message, 
      errorStack: error.stack 
    });
    throw error;
  }
}

/**
 * Start the WebSocket server
 */
function startServer() {
  console.log('🌐 Starting WebSocket server...');
  
  console.log('🔑 Creating HTTPS server...');
  const server = https.createServer({
    key: fs.readFileSync(SSL_KEY_PATH),
    cert: fs.readFileSync(SSL_CERT_PATH)
  });
  console.log('✅ HTTPS server created');

  console.log('🔌 Creating WebSocket server...');
  const wss = new WebSocket.Server({ 
    server,
    path: '/ws'
  });
  console.log('✅ WebSocket server created');

  wss.on('connection', (ws, req) => {
    console.log('🔗 New WebSocket connection received');
    
    stats.totalConnections++;
    stats.activeConnections++;

    const connectionId = `conn-${Date.now()}-${stats.totalConnections}`;
    console.log('🆔 Connection ID:', connectionId);
    console.log('🌍 Client IP:', req.socket.remoteAddress);
    console.log('📊 Connection stats:', {
      total: stats.totalConnections,
      active: stats.activeConnections
    });
    
    log(`New connection: ${connectionId}`, { ip: req.socket.remoteAddress });

    ws.on('message', async (message) => {
      console.log('📨 Message received from client');
      console.log('📝 Message length:', message.length);
      
      try {
        console.log('🔍 Parsing JSON message...');
        const data = JSON.parse(message);
        console.log('✅ JSON parsed successfully');
        console.log('📊 Message data:', data);
        
        log(`Received message:`, data);

        const requestId = data.requestId || 'unknown';
        console.log('🆔 Request ID:', requestId);

        // Handle function registration
        if (data.type === 'register') {
          console.log('📝 REGISTRATION REQUEST received');
          
          const { functionId, functionBody, paramNames = [] } = data;
          console.log('📋 Registration details:');
          console.log('   Function ID:', functionId);
          console.log('   Body length:', functionBody?.length);
          console.log('   Param names:', paramNames);
          
          log(`Processing code registration: ${functionId}`);

          console.log('🔧 Calling registerFunction...');
          const success = registerFunction(functionId, functionBody, paramNames);
          console.log('📊 Registration result:', success);
          
          console.log('📤 Sending registration response...');
          const response = {
            requestId,
            status: 'ok',
            result: { registered: success }
          };
          console.log('📋 Response:', response);
          
          ws.send(JSON.stringify(response));
          console.log('✅ Registration response sent');
        }

        // Handle function execution
        else if (data.type === 'execute') {
          console.log('🚀 EXECUTION REQUEST received');
          
          const { functionId, args = [] } = data;
          console.log('📋 Execution details:');
          console.log('   Function ID:', functionId);
          console.log('   Args count:', args.length);
          console.log('   Args:', args);
          
          log(`Processing code execution: ${functionId}`);

          try {
            console.log('⚡ Starting function execution...');
            
            // Now executeFunction is async and will properly await Promises
            // It also handles encrypted argument decryption
            const result = await executeFunction(functionId, args);
            
            console.log('✅ Function execution completed');
            console.log('📊 Execution result:', result);
            
            log(`Final result to send for ${requestId}:`, result);
            
            console.log('📤 Sending execution response...');
            const response = { 
              requestId, 
              result,
              markerId: data.markerId
            };
            console.log('📋 Response:', response);
            
            ws.send(JSON.stringify(response));
            console.log('✅ Execution response sent');
            
          } catch (execError) {
            console.log('💥 EXECUTION ERROR:', execError);
            console.log('📍 Error message:', execError.message);
            console.log('📍 Error stack:', execError.stack);
            
            log(`Error executing code:`, { error: execError.message, stack: execError.stack });
            
            console.log('📤 Sending error response...');
            const errorResponse = { requestId, error: execError.message };
            console.log('📋 Error response:', errorResponse);
            
            ws.send(JSON.stringify(errorResponse));
            console.log('✅ Error response sent');
          }
        }

        // Unknown message type
        else {
          console.log('❓ Unknown message type:', data.type);
          log(`Unknown message type received`, { type: data.type });
          
          const errorResponse = { requestId, error: `Unknown message type: ${data.type}` };
          ws.send(JSON.stringify(errorResponse));
        }
        
      } catch (error) {
        console.log('💥 MESSAGE PROCESSING ERROR:', error);
        console.log('📍 Error message:', error.message);
        console.log('📍 Raw message:', message.toString().substring(0, 200));
        
        log(`Error processing message:`, { error: error.message, message: message.toString().substring(0, 200) });
        stats.errors++;

        try {
          console.log('📤 Sending generic error response...');
          ws.send(JSON.stringify({ error: error.message }));
          console.log('✅ Generic error response sent');
        } catch (sendError) {
          console.log('💥 Error sending error response:', sendError);
          log(`Error sending error response:`, { error: sendError.message });
        }
      }
    });

    ws.on('close', () => {
      console.log('🔌 WebSocket connection closed:', connectionId);
      stats.activeConnections--;
      console.log('📊 Updated connection stats:', {
        total: stats.totalConnections,
        active: stats.activeConnections
      });
      log(`Connection closed: ${connectionId}`);
    });

    console.log('📤 Sending welcome message...');
    const welcomeMessage = {
      type: 'system',
      message: 'Secure WebSocket server connected',
      connectionId,
      timestamp: new Date().toISOString()
    };
    console.log('📋 Welcome message:', welcomeMessage);
    
    ws.send(JSON.stringify(welcomeMessage));
    console.log('✅ Welcome message sent');
  });

  console.log('🎧 Starting server listener...');
  server.listen(WS_PORT, () => {
    console.log('🎉 SERVER IS LIVE!');
    console.log('🌐 Listening on port:', WS_PORT);
    console.log('🔗 WebSocket path: /ws');
    console.log('🔒 SSL enabled: YES');
    
    log(`Server listening on port ${WS_PORT}`);
  });

  console.log('✅ Server startup complete');
  return wss;
}

// Register a test function
console.log('🧪 Registering test function...');
registerFunction('test', 'return "Hello, World!";', []);
console.log('✅ Test function registered');

// Start server when run directly
if (require.main === module) {
  console.log('🚀 Starting server in standalone mode...');
  log('Starting server...');
  startServer();
  console.log('🎉 Server startup initiated!');
}

// Export functions
console.log('📤 Exporting module functions...');
module.exports = {
  startServer,
  registerFunction,
  executeFunction
};
console.log('✅ Module exports ready');
console.log('🎉 SERVER-FUNCTION-HUB INITIALIZATION COMPLETE!');